<?php

class Queue {
    const TIMEOUT_MULTIPLIER_SECONDS = 1;
    const MAX_TRIES = 10;
    private $db;
    private $dbObj;

    public function __construct($db) {
        $this->db = $db;
        $this->dbObj = new DB_Queue($this->db);
    }

    public function add($service, $method, $obj) {
        $message = serialize($obj);
        //print_r($message);
        $queue = (object)$this->getNewQueue($service, $method, $message);
        $this->dbObj->create($queue);
    }

    private function getNewQueue($service, $method, $message) {
        $arr = array(
            'create_time' => date('Y-m-d H:i:s'),
        	'start_time' => date('Y-m-d H:i:s'),
            'done' => 0,
            'tries' => 0,
            'service' => $service,
            'method' => $method,
            'message' => $message
        );
        return $arr;
    }

    public function runAll() {
        $allJobs = $this->dbObj->getAllNotDone();
        foreach ($allJobs as $job) {
            $this->run($job);
        }
    }

	private function run($job) {
        echo 'Job ID: ' . $job['id'] . ', tries: ' . $job['tries'] . ', start_time: ' . $job['start_time'] . ', class: ' . $job['service'] . ', method: ' . $job['method'] . ': ';

        if (class_exists($job['service'])) {
            $service = new $job['service'];
            if (is_a($service, $job['service']) && method_exists($service, $job['method'])) {
                $obj = unserialize($job['message']);
                if ($service->{$job['method']}($obj)) {
                    $this->setDone($job, $service->result);
                    echo 'Done!';
                } else {
                    $this->postpone($job, $service->result);
                    echo 'Failed!';
                }
                /*
                try {
                    $obj = unserialize($job['message']);
                    $result = $service->{$job['method']}($obj);
                    $this->setDone($job, $result);
                    echo 'Done!';
                } catch (Exception $e) {
                    $this->postpone($job, $e);
                    echo 'Failed!';
                }
                */
            } else {
                echo 'Unknown method';
            }
        } else {
            echo 'Unknown class';
        }
        echo "\n";
    }

    public function setDone($job, $result) {
        $job['done'] = 1;
        $job['tries']++;
        $job['result'] = print_r($result, true);
        $this->dbObj->update((object)$job);
    }

    public function postpone($job, $exception) {
        $job['tries']++;
        if ($job['tries'] > self::MAX_TRIES) {
            $job['discarded'] = 1;
        } else {
            $job['start_time'] = $this->getNextStartTime($job['tries']);
        }
        $job['result'] = print_r($exception, true);
        $this->dbObj->update((object)$job);
    }

    private function getNextStartTime($tries) {
        $time = time() + pow(2, $tries) * self::TIMEOUT_MULTIPLIER_SECONDS;
        return date('Y-m-d H:i:s', $time);
    }
}
